第30章 指针
指针是C++中一种重要的数据类型,它存储了另一个变量的内存地址。通过指针可以直接访问和修改内存中的数据,这使得指针在内存管理、函数参数传递、数组操作等方面有着广泛的应用。本章将详细介绍指针的基本概念、定义、运算及常见用法。
30.1 指针的基本概念
30.1.1 内存地址
计算机的内存被划分为一个个连续的存储单元,每个存储单元都有一个唯一的编号,这个编号就是内存地址。内存地址通常用十六进制数表示,例如0x7ffd9a3b45c0。
30.1.2 指针的定义
指针是一个变量,它的值是另一个变量的内存地址。通过指针,我们可以间接访问它所指向的变量。
例如,若变量a的内存地址是0x7ffd9a3b45c0,那么指向a的指针p的值就是0x7ffd9a3b45c0。
30.2 指针的定义与初始化
30.2.1 指针的定义语法
数据类型* 指针变量名;
- 数据:指针所指向变量的数据类型,决定访问内存时的字节长度与解析方式。
*:标识该变量为指针。- 指针变量名:遵循标识符命名规则。
示例:
int* p; // 指向int的指针
double* dp;// 指向double的指针
char* cp; // 指向char的指针
30.2.2 指针的初始化
指针使用前必须初始化,否则为野指针,访问会导致程序崩溃。
- 指向普通变量(取地址符
&)
int a = 10;
int* p = &a; // p存储a的内存地址
- 初始化为空指针
int* p1 = nullptr; // C++11标准空指针,推荐
int* p2 = 0;
int* p3 = NULL; // 需要引入<cstdio>
- 指向new动态分配内存
int* p = new int;
30.3 指针的解引用
解引用运算符*,通过指针访问其指向的变量:
int a = 10;
int* p = &a;
cout << *p << endl; // 输出10,读取a的值
*p = 20; // 通过指针修改a
cout << a << endl; // 输出20
30.4 指针的运算
30.4.1 指针±整数
指针加减整数,代表向前/向后移动对应个数的该类型变量,移动单位为类型字节大小。
int arr[] = {10,20,30,40,50};
int* p = arr; // p指向arr[0]
p += 2; // 向后移动2个int,指向arr[2]
cout << *p; // 输出30
p--; // 向前移动1个int,指向arr[1]
cout << *p; // 输出20
30.4.2 指针相减
仅同数组内指针可相减,结果为两个指针间元素个数。
30.5 指针数组
数组中每个元素都是指针:
int a=10,b=20,c=30;
int* arr[3] = {&a, &b, &c};
for(int i=0;i<3;i++){
cout << *arr[i] << " "; // 输出10 20 30
}
30.6 指针与函数
30.6.1 指针作函数参数
可在函数内部修改外部实参,无需拷贝整个变量:
void swap(int* x,int* y){
int temp = *x;
*x = *y;
*y = temp;
}
int main(){
int a=10,b=20;
swap(&a,&b);
cout << a << " " << b; // 输出20 10
return 0;
}
30.6.2 指针作为返回值
可返回动态分配内存地址,禁止返回局部变量地址(局部变量函数结束销毁,地址失效):
int* createInt(int value){
int* p = new int;
*p = value;
return p;
}
int main(){
int* res = createInt(100);
cout << *res;
delete res; // 释放内存
return 0;
}
30.6.3 函数指针
存储函数地址,可通过指针调用函数,语法:返回类型 (*指针名)(参数列表)
int add(int a,int b){return a+b;}
int sub(int a,int b){return a-b;}
int main(){
int (*funcPtr)(int,int);
funcPtr = add;
cout << funcPtr(3,5); // 输出8
funcPtr = sub;
cout << funcPtr(3,5); // 输出-2
return 0;
}
30.7 const与指针三种组合
30.7.1 指向常量的指针 const int* p
指针可改指向,不能通过*p修改值:
int a=10,b=20;
const int* p = &a;
// *p = 30; 报错
p = &b; // 合法,更换指向
30.7.2 常量指针 int* const p
指针指向固定不可改,可修改指向内容:
int a=10;
int* const p = &a;
*p = 30; // 合法
// p = &b; 报错
30.7.3 指向常量的常量指针 const int* const p
指针不可换指向,也不能修改内容:
int a=10;
const int* const p = &a;
// *p = 20; 报错
// p = &b; 报错
30.8 动态内存分配(new / delete)
30.8.1 单个变量分配释放
int* p = new int;
*p = 100;
cout << *p;
delete p;
p = nullptr; // 置空防野指针
30.8.2 动态数组分配释放
int n = 5;
int* arr = new int[n];
for(int i=0;i<n;i++) arr[i] = i*10;
delete[] arr; // 数组必须delete[]
arr = nullptr;
30.8.3 注意事项
- new分配内存必须配对delete,数组用
delete[]; - 释放后指针赋值
nullptr; - 禁止重复释放同一块内存。
30.9 指针常见错误
- 野指针:未初始化/释放后未置空,访问程序崩溃;
- 空指针解引用:
int* p=nullptr; *p;直接崩溃; - 指针越界:访问数组范围外地址;
- 内存泄漏:new后忘记delete,内存永久占用; 泄漏示例:
void leak1(){
int* p = new int(100);
// 无delete,内存泄漏
}
void leak2(){
int* p = new int(200);
p = new int(300); // 原地址丢失,无法释放
delete p;
}
避免方法:分配后在所有分支执行释放,优先使用智能指针unique_ptr。